﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using SlimDX.Direct3D9;
using SlimDX;

namespace Gype.UI.D3D9
{
    internal class Rendertarget9 : Rendertarget
    {
        public Rendertarget9(Device targetDevice, Viewport dstViewport)
        {
            mViewport = dstViewport;
            mDevice = targetDevice;
            Surface backBuffer = targetDevice.GetBackBuffer(0, 0);

            mTargetTexture = new Texture(
                mDevice,
                backBuffer.Description.Width,
                backBuffer.Description.Height,
                1,
                Usage.RenderTarget,
                backBuffer.Description.Format,
                Pool.Default
            );

            mViewport = new Viewport(0, 0, backBuffer.Description.Width, backBuffer.Description.Height);

            mTargetSurface = mTargetTexture.GetSurfaceLevel(0);
           
            backBuffer.Dispose();

            mDeclaration = new VertexDeclaration(targetDevice,
                new VertexElement[]
                {
                    new VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0),
                    new VertexElement(0, 12, DeclarationType.Color, DeclarationMethod.Default, DeclarationUsage.Color, 0),
                    new VertexElement(0, 16, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0),
                    new VertexElement(0, 24, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 1),
                    VertexElement.VertexDeclarationEnd
                }
            );

            mShader = new Shader9(targetDevice);

            mScreenMatrix = Matrix.Identity;
            //mScreenMatrix = Matrix.LookAtLH(new Vector3(mViewport.Width / 2.0f, mViewport.Height / 2.0f, -1), new Vector3(mViewport.Width / 2.0f, mViewport.Height / 2.0f, 0), new Vector3(0, 1, 0));
            mScreenMatrix *= Matrix.OrthoLH(mViewport.Width, mViewport.Height, 0, 1);

            mDevice.SetRenderState(RenderState.AlphaBlendEnable, true);
            mDevice.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha);
            mDevice.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
            mDevice.SetRenderState(RenderState.CullMode, Cull.None);

            mRectDrawProvider = new RectangleProvider(this, mDevice);
            mEllipseProvider = new EllipseProvider(mDevice, this);
            mRoundRectProvider = new RoundRectProvider(mDevice, this);
            mArcProvider = new ArcProvider(mDevice, this);
            mTriProvider = new TriangleProvider(this);

            mTexSprite = new Sprite(mDevice);
            mTextSprite = new Sprite(mDevice);

            Transform = LinearMatrix.Identity;

            Viewport = new Vector2(mViewport.Width, mViewport.Height);
        }

        public override void SetClipEnabled(bool enabled)
        {
            mDevice.SetRenderState(RenderState.ScissorTestEnable, enabled);
        }

        public override void BeginUI()
        {
            if (mBaseRendertarget != null)
                mBaseRendertarget.Dispose();

            mBaseRendertarget = mDevice.GetRenderTarget(0);

            mDevice.SetRenderTarget(0, mTargetSurface);
            mDevice.Clear(ClearFlags.Target, 0, 0, 0);

            mDevice.SetRenderState(RenderState.SourceBlend, Blend.SourceAlpha);
            mDevice.SetRenderState(RenderState.DestinationBlend, Blend.InverseSourceAlpha);

            //mTextSprite.Begin(SpriteFlags.AlphaBlend);
        }

        public override void EndUI()
        {
            //mTextSprite.End();
            mDevice.SetRenderTarget(0, mBaseRendertarget);

            mTexSprite.Begin(SpriteFlags.AlphaBlend);
            mTexSprite.Draw(mTargetTexture, new Color4(1, 1, 1, 1));
            mTexSprite.End();
        }

        public override void FillTriangle(Vector2 p1, Vector2 p2, Vector2 p3, UI.Brush color)
        {
            mTriProvider.FillTriangles(new Vector2[] { p1, p2, p3 }, color as Brush);
        }

        public override void FillRectangle(Vector2 position, Vector2 size, Gype.UI.Brush color)
        {
            mRectDrawProvider.Fill(position, size, color as Brush);
        }

        public override void DrawRectangle(Vector2 position, Vector2 size, Gype.UI.Brush color, float strokeWidth = 1, RectangleFillMask mask = RectangleFillMask.All)
        {
            mRectDrawProvider.DrawToTarget(strokeWidth, position, size, color as Brush, mask);
        }

        public override void FillEllipse(Ellipse ellipse, Color4 color)
        {
            if (ellipse.HorizontalRadius == 0 && ellipse.VerticalRadius == 0)
                throw new ArgumentException("Attempted to draw an ellipse with no radius!");

            mEllipseProvider.Fill(ellipse.HorizontalRadius, ellipse.VerticalRadius, ellipse.Center, color);
        }

        public override void DrawEllipse(Ellipse ellipse, Color4 color, float stroke)
        {
            mEllipseProvider.Draw(ellipse.HorizontalRadius, ellipse.VerticalRadius, ellipse.Center, color, stroke);
        }

        public override void FillRoundedRectangle(Vector2 position, Vector2 size, float horizontalRadius, float verticalRadius, UI.Brush color)
        {
            mRoundRectProvider.Fill(position, size, horizontalRadius, verticalRadius, color as Brush);
        }

        public override void DrawRoundedRectangle(Vector2 position, Vector2 size, float horizontalRadius, float verticalRadius, UI.Brush color, float strokeWidth = 1)
        {
            mRoundRectProvider.Draw(position, size, horizontalRadius, verticalRadius, color as Brush, strokeWidth);
        }

        public override void DrawArc(Ellipse ellipse, Color4 color, float stroke, float angleStart, float angleEnd)
        {
            mArcProvider.Draw(ellipse.HorizontalRadius, ellipse.VerticalRadius, ellipse.Center, color, stroke, angleStart, angleEnd);
        }

        public override void DrawEllipseFourth(Ellipse ellipse, Color4 color, float stroke, ArcQuadrant quadrant)
        {
            throw new NotImplementedException();
        }

        public override void DrawEllipseHalf(Ellipse ellipse, Color4 color, float stroke, ArcQuadrant quadrant)
        {
            if (quadrant != ArcQuadrant.Lower && quadrant != ArcQuadrant.Upper)
                throw new ArgumentException("Invalid quadrant specified!");

            float angleMin = 0;
            float angleMax = 180.0f;
            if (quadrant == ArcQuadrant.Upper)
            {
                angleMin = 180.0f;
                angleMax = 0.0f;
            }

            mArcProvider.Draw(ellipse.HorizontalRadius, ellipse.VerticalRadius, ellipse.Center, color, stroke, angleMin, angleMax);
        }

        public override void DrawImage(Image image, System.Drawing.RectangleF destRectangle)
        {
            DrawImage(image, destRectangle, new System.Drawing.RectangleF(0, 0, image.Width, image.Height));
        }

        public override void DrawImage(Image image, System.Drawing.RectangleF destRectangle, System.Drawing.RectangleF srcRectangle)
        {
            Image9 img = image as Image9;
            img.DrawImage(srcRectangle, destRectangle);
        }

        public override void PushClipArea(System.Drawing.RectangleF area)
        {
            Vector2 start = new Vector2(area.X, area.Y);
            Vector2 end = new Vector2(area.X + area.Width, area.Y + area.Height);

            start = Transform.TransformPoint(start);
            end = Transform.TransformPoint(end);

            mClipStack.Push(area);
            mDevice.ScissorRect = new System.Drawing.Rectangle((int)start.X, (int)start.Y, (int)(end.X - start.X), (int)(end.Y - start.Y));
            mDevice.SetRenderState(RenderState.ScissorTestEnable, true);
        }

        public override void PopClipArea()
        {
            mClipStack.Pop();
            if (mClipStack.Count == 0)
            {
                mDevice.ScissorRect = System.Drawing.Rectangle.Empty;
                mDevice.SetRenderState(RenderState.ScissorTestEnable, false);
            }
            else
                mDevice.ScissorRect = System.Drawing.Rectangle.Truncate(mClipStack.Peek());
        }

        internal override System.Drawing.Size GetMaximumTextureSize()
        {
            var caps = mDevice.Capabilities;
            return new System.Drawing.Size(caps.MaxTextureWidth, caps.MaxTextureHeight);
        }

        internal override Image CreateImage(System.Drawing.Bitmap bmp)
        {
            return new Image9(this, bmp);
        }

        internal override UI.Brush CreateSolidColorBrush(Color4 color)
        {
            return new SolidColorBrush(this, color);
        }

        internal override UI.Brush CreateLinearGradient(LinearGradientDescription gradientDescription)
        {
            return new LinearGradientBrush(this, gradientDescription);
        }

        internal override UI.Brush CreateRadialGradient(RadialGradientDescription gDesc)
        {
            return new RadialGradientBrush(this, gDesc);
        }

        public override UI.SpriteFont CreateFont(string familyName, int pixelSize)
        {
            return new SpriteFont(this, mTextSprite, familyName, pixelSize);
        }

        internal UIVertexText[] CreateTriangleListText(Vector2 position, Vector2 size, Color4 color, Vector2 brushMin, Vector2 brushMax)
        {
            return new UIVertexText[]
            {
                new UIVertexText()
                {
                    Position = new Vector3(position.X, position.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(0, 0),
                    TexCoordBrush = brushMin,
                },

                new UIVertexText()
                {
                    Position = new Vector3(position.X + size.X, position.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(1, 0),
                    TexCoordBrush = new Vector2(brushMax.X, brushMin.Y),
                },

                new UIVertexText()
                {
                    Position = new Vector3(position.X, position.Y + size.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(0, 1),
                    TexCoordBrush = new Vector2(brushMin.X, brushMax.Y)
                },

                new UIVertexText()
                {
                    Position = new Vector3(position.X + size.X, position.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(1, 0),
                    TexCoordBrush = new Vector2(brushMax.X, brushMin.Y),
                },

                new UIVertexText()
                {
                    Position = new Vector3(position.X + size.X, position.Y + size.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(1, 1),
                    TexCoordBrush = brushMax,
                },

                new UIVertexText()
                {
                    Position = new Vector3(position.X, position.Y + size.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(0, 1),
                    TexCoordBrush = new Vector2(brushMin.X, brushMax.Y)
                },              
            };
        }

        internal UIVertex[] CreateTriangleFan(Vector2 position, Vector2 size, Color4 color)
        {
            return new UIVertex[]
            {
                new UIVertex()
                {
                    Position = new Vector3(position.X, position.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(0, 0),
                },

                new UIVertex()
                {
                    Position = new Vector3(position.X + size.X, position.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(1, 0),
                },

                new UIVertex()
                {
                    Position = new Vector3(position.X + size.X, position.Y + size.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(1, 1),
                },

                new UIVertex()
                {
                    Position = new Vector3(position.X, position.Y + size.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(0, 1),
                }
            };
        }

        internal UIVertex[] CreateTriangleList(Vector2 position, Vector2 size, Color4 color)
        {
            return new UIVertex[]
            {
                new UIVertex()
                {
                    Position = new Vector3(position.X, position.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(0, 0),
                },

                new UIVertex()
                {
                    Position = new Vector3(position.X + size.X, position.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(1, 0),
                },

                new UIVertex()
                {
                    Position = new Vector3(position.X, position.Y + size.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(0, 1),
                },

                new UIVertex()
                {
                    Position = new Vector3(position.X + size.X, position.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(1, 0),
                },

                new UIVertex()
                {
                    Position = new Vector3(position.X + size.X, position.Y + size.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(1, 1),
                },

                new UIVertex()
                {
                    Position = new Vector3(position.X, position.Y + size.Y, 0),
                    Color = color.ToArgb(),
                    TexCoord = new Vector2(0, 1),
                },
            };
        }

        internal Matrix OrthoMatrix { get { return mScreenMatrix; } }
        internal VertexDeclaration UIVertexDeclaration { get { return mDeclaration; } }
        internal Device GraphicsDevice { get { return mDevice; } }

        private RectangleProvider mRectDrawProvider;
        private EllipseProvider mEllipseProvider;
        private RoundRectProvider mRoundRectProvider;
        private ArcProvider mArcProvider;
        private TriangleProvider mTriProvider;

        private Stack<System.Drawing.RectangleF> mClipStack = new Stack<System.Drawing.RectangleF>();
        private VertexDeclaration mDeclaration;
        private Shader9 mShader;
        private Device mDevice;
        private Texture mTargetTexture;
        private Surface mTargetSurface;
        private Viewport mViewport;
        private Surface mBaseRendertarget;
        private Matrix mScreenMatrix;
        private Sprite mTexSprite;
        private Sprite mTextSprite;
    }
}
